package com.heatup.apt.common.util;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import java.awt.image.BufferedImage;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by blank on 2017/4/11.
 */
public class QRCodeUtil {

    public static final Logger log = LoggerFactory.getLogger(QRCodeUtil.class);

    private static final int width = 300;//默认二维码宽度
    private static final int height = 300;//默认二维码高度
    private static final String format = "png";//默认二维码文件格式
    private static final Map<EncodeHintType, Object> hints = new HashMap<>();//二维码参数

    private static String pad(String str, int size, boolean isPrefixed) {
        if (str == null)
            str = "";
        int str_size = str.length();
        int pad_len = size - str_size;
        StringBuilder retValue = new StringBuilder();
        for (int i = 0; i < pad_len; i++) {
            retValue.append("0");
        }
        if (isPrefixed)
            return retValue.append(str).toString();
        return retValue.insert(0, str).toString();
    }

    private static int[] encrypt(int[] v, int[] k, int n) {
        int y;
        int p;
        int rounds = 6 + 52 / n;
        int sum = 0;
        int z = v[n - 1];
        int delta = 0x9E3779B9;
        do {
            sum += delta;
            int e = (sum >>> 2) & 3;
            for (p = 0; p < n - 1; p++) {
                y = v[p + 1];
                z = v[p] += (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
            }
            y = v[0];
            z = v[n - 1] += (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
        } while (--rounds > 0);

        return v;

    }

    private static int[] decrypt(int[] v, int[] k, int n) {

        int z, y, delta = 0x9E3779B9, sum, e;
        int p;
        int rounds = 6 + 52 / n;
        sum = rounds * delta;
        y = v[0];
        do {
            e = (sum >>> 2) & 3;
            for (p = n - 1; p > 0; p--) {
                z = v[p - 1];
                y = v[p] -= (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
            }
            z = v[n - 1];
            y = v[0] -= (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
        } while ((sum -= delta) != 0);
        return v;
    }

    /**
     * 二维码门禁加解密
     */
    private static final int[] KEY = new int[]{//XXTEA encrypt KEY
            0x00543210, 0x00543210,
            0x00543210, 0x00543210
//            0x0E816FD3, 0x0E816FD3,
//            0x0E816FD3, 0x0E816FD3
    };
    private static final SimpleDateFormat sdfTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /*
        public static String getTime() {
            return sdfTime.format(new Date());
        }
        public static String getTimebefore5minute() {
            return sdfTime.format(new Date().getTime()-600000);
        }
    */
    static {
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");//字符编码
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);//容错等级 L、M、Q、H 其中L为最低,H为最高
        hints.put(EncodeHintType.MARGIN, 1);//二维码与图片边距
    }

    /**
     * 返回一个 BufferedImage 对象
     *
     * @param content 二维码内容
     * @param width   宽
     * @param height  高
     */
    public static BufferedImage toBufferedImage(String content, int width, int height) throws WriterException, IOException {
        BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
        return MatrixToImageWriter.toBufferedImage(bitMatrix);
    }

    /**
     * 在指定路径生成二维码图片并返回生成结果
     *
     * @param content 二维码内容
     * @param imgPath 生成二维码文件的位置
     * @param width   宽
     * @param height  高
     */
    public static boolean toQRImgFile(String content, String imgPath, int width, int height) throws WriterException, IOException {
        BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
        return ImageIO.write(MatrixToImageWriter.toBufferedImage(bitMatrix), "png", new File(imgPath));
    }

    /**
     * 将二维码图片输出到一个流中
     *
     * @param content 二维码内容
     * @param stream  输出流
     * @param width   宽
     * @param height  高
     */
    public static void writeToStream(String content, OutputStream stream, int width, int height) throws WriterException, IOException {
        BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
        MatrixToImageWriter.writeToStream(bitMatrix, format, stream);
    }

    /**
     * 生成二维码图片文件
     *
     * @param content 二维码内容
     * @param path    文件保存路径
     * @param width   宽
     * @param height  高
     */
    public static void createQRCode(String content, String path, int width, int height) throws WriterException, IOException {
        BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
        //toPath() 方法由jdk1.7及以上提供
        MatrixToImageWriter.writeToPath(bitMatrix, format, new File(path).toPath());
    }

    /**
     * 生成教练签到二维码 并上传至OSS 最后返回OSS URL
     *
     * @param content 二维码图片的内容
     * @return OSS中的图片
     */
    public static String createSignInQRCode(String content) {

        String userHomePath = System.getProperty("user.home");
        String imgPath = userHomePath + File.separator + "test.png";

        boolean result = false;
        try {
            result = QRCodeUtil.toQRImgFile(content, imgPath, width, height);
        } catch (WriterException | IOException e) {
            e.printStackTrace();
            log.error("生成教练签到二维码出错 blank - -! ---> " + e.toString() + e.getCause());
        }
        String ossPath = null;
        if (result) {
            //生成成功! 上传到OSS
            try {
                File uploadFile = new File(imgPath);

                ossPath = ImgUploadToOSSUtils.upload(uploadFile);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("上传生成的二维码到OSS时 出错 ---> " + e.toString() + e.getCause());
            }
        }
        return ossPath;
    }


    /**
     * 生成门禁二维码
     *
     * @param userId 自定义数据
     * @param num    密码
     * @return true 生成成功 / false 生成失败
     */
    public static String createQRCode(String userId, String num) {


//        String userId = "2" + "|";//用户自定义数据" 暂时打算存放 userId 来方便后期校验
        userId += "|";
//        String num = "00000004";    //关联卡号 8位 16进制 ,生成的二维码如果要实现限次功能，必须使此ID是递增的
        String userKey = "00852953";//用户秘钥

        String openTimeLimit = "00";//开门时间限制 00表示无时间限制 01表示有时间限制
        String openTimes = "10";    //开门次数 2位  取值范围00-FF 00表示没有次数限制 01-FF  ( 1-255次)
        /**
         * 开门校验模式
         *
         * 00表示不限
         * 01表示以 项目编号+楼栋编号参数匹配来开门
         * 02表示以 MAC 地址后6位匹配方式开门
         * 03表示以 MAC 地址后4位匹配方式开门
         */
        String checkType = "03";    //开门校验模式 00表示不限 01表示以 项目编号+楼栋编号参数匹配来开门 02表示以 MAC 地址后6位匹配方式开门 03表示以 MAC 地址后4位匹配方式开门
        String openTime = "0000";   //开门时长 0000表示默认


        String[] strQRArray = {"1", "2", "3", "4", "5", "6", "7", "8"};

        strQRArray[0] = userId;
        strQRArray[1] = num;
        strQRArray[3] = userKey;

        String keepStr = "000000";//保留字节

        strQRArray[4] = openTimeLimit + openTimes + checkType + openTime + keepStr;//管理字节

        //得到起始时间和终止时间，本例中是起始时间是当前时间，终止时间当前时间+三天后的时间
        //起始时间为当前时间提前10分钟，这样免去了1分钟的问题
        Date dateNow = new Date();
        SimpleDateFormat df = new SimpleDateFormat("yyMMddHHmmss");//设置日期格式
        String startDateStr = df.format(dateNow.getTime() - 600000);//当前时间减10分钟，免得有未到生效时间的

        String endDateStr = df.format(DateUtils.addDays(dateNow, 1));//当前设置二维码24小时内有效 //TODO 需要修改

        strQRArray[5] = startDateStr + endDateStr;//有效开始+结束时间

        /**
         * 楼栋或MAC地址数量： 2位 取值范围00-FF 00 所有楼栋或 MAC设备都可以开  01-FF  ( 1-255个)
         *  00  所有楼栋或MAC设备都可以开
         *  02  可开楼栋或MAC为2个，有2个楼栋或MAC的编号
         */
        String checkDeviceNum = "04";
        String DeviceMac1 = "2008";//前面数值如果为00 而此字段无效 如果大于01~FF 就得根据checkType 02/03的值来填写对应长度的 MAC 后6/4位数值
        String DeviceMac3 = "2228";
        String DeviceMac2 = "2233";
        String DeviceMac4 = "2245";
        strQRArray[6] = checkDeviceNum + DeviceMac1 + DeviceMac2 + DeviceMac3 + DeviceMac4;

        //需要加密的字符串
        String encryptCodeStr = strQRArray[4] + strQRArray[5] + strQRArray[6];

        //encryptCodeStr="010302000000000017041116080717041416080702920992";
        //得到加密长度 2位
        int encryptCodeLength = encryptCodeStr.length();
        byte encryptLength = (byte) (encryptCodeLength / 8);//XXTEA加密的整形个数=加密字符串长度/8;如果对8取余，余数不为0则+1
        byte nRemainder1 = (byte) (encryptCodeLength % 8);
        if (nRemainder1 != 0) {
            encryptLength++;
        }

        int v = ((int) encryptLength) & 0xFF;
        String strTmp = Integer.toHexString(v);
        if (strTmp.length() < 2) {
            strTmp = "0" + strTmp;
        }

        //得到加密字符串余数 加密字符串对8取余的一半
        byte nRemainder = (byte) ((encryptCodeLength % 8) / 2);

        v = ((int) nRemainder) & 0xFF;
        String strTmp2 = Integer.toHexString(v);
        if (strTmp2.length() < 2) {
            strTmp2 = "0" + strTmp2;
        }
        //得到加密长度以及余数
        strQRArray[2] = strTmp + strTmp2;
        //将字符串转成16进制的整形数
        //先转前面前面8的整数个数，再转最后一个整数
        int[] EnCodeInt = new int[30];

        int tmp1;
        int tmp2;
        String strTmp1;
        String strTmp4;
        String strTmp5;
        if (nRemainder != 0) {
            //将字符串分中化成一个一个整形值，由于JAVA Integer.valueOf（）整形值范围受限，将整形值分为二个4字符值处理
            for (int k = 0; k < encryptLength - 1; k++) {
                strTmp4 = encryptCodeStr.substring(k * 8, k * 8 + 4);
                strTmp5 = encryptCodeStr.substring(k * 8 + 4, (k + 1) * 8);
                tmp1 = Integer.valueOf(strTmp4, 16);     //00852953
                tmp2 = Integer.valueOf(strTmp5, 16);
                EnCodeInt[k] = (tmp1 << 16) | tmp2;
            }
            EnCodeInt[encryptLength - 1] = Integer.valueOf(encryptCodeStr.substring((encryptLength - 1) * 8, (encryptLength - 1) * 8 + nRemainder1), 16);
        } else {
            for (int k = 0; k < encryptLength; k++) {
                strTmp4 = encryptCodeStr.substring(k * 8, k * 8 + 4);
                strTmp5 = encryptCodeStr.substring(k * 8 + 4, (k + 1) * 8);
                tmp1 = Integer.valueOf(strTmp4, 16);     //00852953
                tmp2 = Integer.valueOf(strTmp5, 16);
                EnCodeInt[k] = (tmp1 << 16) | tmp2;
            }
        }
        //设置用户秘钥

        tmp1 = Integer.valueOf(userKey.substring(0, 4), 16);     //00852953
        tmp2 = Integer.valueOf(userKey.substring(4), 16);
        KEY[0] = (tmp1 << 16) | tmp2;
        KEY[2] = KEY[0];

        int[] EnCodeResult;

        EnCodeResult = encrypt(EnCodeInt, KEY, encryptLength);//加密

        StringBuilder EnQRResult = new StringBuilder();
        for (int k = 0; k < encryptLength; k++) {
            strTmp1 = pad(Integer.toHexString(EnCodeResult[k]).toUpperCase(), 8, true);
            EnQRResult.append(strTmp1);
        }
        System.out.println("加密前字符串" + encryptCodeStr);
        System.out.println("加密后的字符串" + EnQRResult);

        //测试，将加密的字符串解码出来
        int[] DecodeInt;
        DecodeInt = decrypt(EnCodeResult, KEY, encryptLength);
        StringBuilder DeQRResult = new StringBuilder();
        //解码后的字符串，先解前面的，最后再解最后一个整形值外，最后一个整形数，
        //如果位数不是8的整数，有余数，则要根据二维码余数参数，去掉前面的多余的00

        for (int k = 0; k < encryptLength - 1; k++) {
            strTmp1 = pad(Integer.toHexString(DecodeInt[k]).toUpperCase(), 8, true);
            DeQRResult.append(strTmp1);
        }
        if (nRemainder == 1) {
            //余数为1，表示最后一个字符串，有2位

            strTmp1 = pad(Integer.toHexString(DecodeInt[encryptLength - 1]).toUpperCase(), 2, true);
            DeQRResult.append(strTmp1);
        } else if (nRemainder == 2) {
            //余数为2，表示最后一个字符串，有4位

            strTmp1 = pad(Integer.toHexString(DecodeInt[encryptLength - 1]).toUpperCase(), 4, true);
            DeQRResult.append(strTmp1);
        } else if (nRemainder == 3) {
            //余数为3，表示最后一个字符串，有6位

            strTmp1 = pad(Integer.toHexString(DecodeInt[encryptLength - 1]).toUpperCase(), 6, true);
            DeQRResult.append(strTmp1);
        } else {
            //余数为0，表示最后一个字符串，有8位，是完整的

            strTmp1 = pad(Integer.toHexString(DecodeInt[encryptLength - 1]).toUpperCase(), 8, true);
            DeQRResult.append(strTmp1);
        }

        System.out.println("解密后字符串" + DeQRResult);

        String QREndStr = strQRArray[0] + strQRArray[1] + strQRArray[2] + strQRArray[3] + EnQRResult;

        System.out.println("完整二维码字符串：" + QREndStr);//1|00000001070300852953FCB0BFB18A162371996BCF295B3477E06C1C35990385A7CBBD04C945

        return QREndStr;
    }

    /**
     * 生成二维码
     *
     * @param userId    用户 ID 标识
     * @param macLast4  可离线开门的设备的 MAC后4位
     * @param pass      当天不重复的密码
     * @param startDate 密码有效开始时间
     * @param endDate   密码有效结束时间
     * @return 二维码
     * @author blank 2017/10/30 下午3:59
     */
    public static String createQRCode(String userId, String macLast4, String pass, Date startDate, Date endDate) {


//        String userId = "2" + "|";//用户自定义数据" 暂时打算存放 userId 来方便后期校验
        userId += "|";
//        String pass = "00000004";    //关联卡号 8位 16进制 ,生成的二维码如果要实现限次功能，必须使此ID是递增的
        String userKey = "00852953";//用户秘钥

        String openTimeLimit = "01";//开门时间限制 00表示无时间限制 01表示有时间限制
        String openTimes = "10";    //开门次数 2位  取值范围00-FF 00表示没有次数限制 01-FF  ( 1-255次)
        /**
         * 开门校验模式
         *
         * 00表示不限
         * 01表示以 项目编号+楼栋编号参数匹配来开门
         * 02表示以 MAC 地址后6位匹配方式开门
         * 03表示以 MAC 地址后4位匹配方式开门
         */
        String checkType = "03";    //开门校验模式 00表示不限 01表示以 项目编号+楼栋编号参数匹配来开门 02表示以 MAC 地址后6位匹配方式开门 03表示以 MAC 地址后4位匹配方式开门
        String openTime = "0000";   //开门时长 0000表示默认


        String[] strQRArray = {"1", "2", "3", "4", "5", "6", "7", "8"};

        strQRArray[0] = userId;
        strQRArray[1] = pass;
        strQRArray[3] = userKey;

        String keepStr = "000000";//保留字节

        strQRArray[4] = openTimeLimit + openTimes + checkType + openTime + keepStr;//管理字节

        //得到起始时间和终止时间，本例中是起始时间是当前时间，终止时间当前时间+三天后的时间
        //起始时间为当前时间提前10秒钟

        SimpleDateFormat df = new SimpleDateFormat("yyMMddHHmmss");//设置日期格式
        if(startDate == null)
            startDate = new Date();
        String startDateStr = df.format(startDate.getTime() - 10000);//当前时间减10秒钟

        endDate =  endDate != null? endDate : DateUtil.addMinites(startDate,2 );
        String endDateStr = df.format(endDate);//当前设置二维码订单结束时间内有效

        strQRArray[5] = startDateStr + endDateStr;//有效开始+结束时间

        /**
         * 楼栋或MAC地址数量： 2位 取值范围00-FF 00 所有楼栋或 MAC设备都可以开  01-FF  ( 1-255个)
         *  00  所有楼栋或MAC设备都可以开
         *  02  可开楼栋或MAC为2个，有2个楼栋或MAC的编号
         */
//        String checkDeviceNum = "04";
//        String deviceMac1 = "2008";//前面数值如果为00 而此字段无效 如果大于01~FF 就得根据checkType 02/03的值来填写对应长度的 MAC 后6/4位数值
//        String deviceMac2 = "2228";
//        String deviceMac3 = "2233";
//        String deviceMac4 = "2245";

//        strQRArray[6] = checkDeviceNum + deviceMac1 + deviceMac2 + deviceMac3 + deviceMac4;

        strQRArray[6] = "01" + macLast4; //只有一个门禁能打开
        if (macLast4 == null) {//正式环境不可能遇到 macLast4==null 说明有 storeId 但却没有对应的门禁设备
            strQRArray[6] = "00";
        }

        //需要加密的字符串
        String encryptCodeStr = strQRArray[4] + strQRArray[5] + strQRArray[6];

        //encryptCodeStr="010302000000000017041116080717041416080702920992";
        //得到加密长度 2位
        int encryptCodeLength = encryptCodeStr.length();
        byte encryptLength = (byte) (encryptCodeLength / 8);//XXTEA加密的整形个数=加密字符串长度/8;如果对8取余，余数不为0则+1
        byte nRemainder1 = (byte) (encryptCodeLength % 8);
        if (nRemainder1 != 0) {
            encryptLength++;
        }

        int v = ((int) encryptLength) & 0xFF;
        String strTmp = Integer.toHexString(v);
        if (strTmp.length() < 2) {
            strTmp = "0" + strTmp;
        }

        //得到加密字符串余数 加密字符串对8取余的一半
        byte nRemainder = (byte) ((encryptCodeLength % 8) / 2);

        v = ((int) nRemainder) & 0xFF;
        String strTmp2 = Integer.toHexString(v);
        if (strTmp2.length() < 2) {
            strTmp2 = "0" + strTmp2;
        }
        //得到加密长度以及余数
        strQRArray[2] = strTmp + strTmp2;
        //将字符串转成16进制的整形数
        //先转前面前面8的整数个数，再转最后一个整数
        int[] EnCodeInt = new int[30];

        int tmp1;
        int tmp2;
        String strTmp1;
        String strTmp4;
        String strTmp5;
        if (nRemainder != 0) {
            //将字符串分中化成一个一个整形值，由于JAVA Integer.valueOf（）整形值范围受限，将整形值分为二个4字符值处理
            for (int k = 0; k < encryptLength - 1; k++) {
                strTmp4 = encryptCodeStr.substring(k * 8, k * 8 + 4);
                strTmp5 = encryptCodeStr.substring(k * 8 + 4, (k + 1) * 8);
                tmp1 = Integer.valueOf(strTmp4, 16);     //00852953
                tmp2 = Integer.valueOf(strTmp5, 16);
                EnCodeInt[k] = (tmp1 << 16) | tmp2;
            }
            EnCodeInt[encryptLength - 1] = Integer.valueOf(encryptCodeStr.substring((encryptLength - 1) * 8, (encryptLength - 1) * 8 + nRemainder1), 16);
        } else {
            for (int k = 0; k < encryptLength; k++) {
                strTmp4 = encryptCodeStr.substring(k * 8, k * 8 + 4);
                strTmp5 = encryptCodeStr.substring(k * 8 + 4, (k + 1) * 8);
                tmp1 = Integer.valueOf(strTmp4, 16);     //00852953
                tmp2 = Integer.valueOf(strTmp5, 16);
                EnCodeInt[k] = (tmp1 << 16) | tmp2;
            }
        }
        //设置用户秘钥

        tmp1 = Integer.valueOf(userKey.substring(0, 4), 16);     //00852953
        tmp2 = Integer.valueOf(userKey.substring(4), 16);
        KEY[0] = (tmp1 << 16) | tmp2;
        KEY[2] = KEY[0];

        int[] EnCodeResult;

        EnCodeResult = encrypt(EnCodeInt, KEY, encryptLength);//加密

        StringBuilder EnQRResult = new StringBuilder();
        for (int k = 0; k < encryptLength; k++) {
            strTmp1 = pad(Integer.toHexString(EnCodeResult[k]).toUpperCase(), 8, true);
            EnQRResult.append(strTmp1);
        }
//        System.out.println("加密前密码" + pass);
//        System.out.println("加密前字符串" + encryptCodeStr);
//        System.out.println("加密后的字符串" + EnQRResult);

        //测试，将加密的字符串解码出来
//        int[] DecodeInt;
//        DecodeInt = decrypt(EnCodeResult, KEY, encryptLength);
//        StringBuilder DeQRResult = new StringBuilder();
//        //解码后的字符串，先解前面的，最后再解最后一个整形值外，最后一个整形数，
//        //如果位数不是8的整数，有余数，则要根据二维码余数参数，去掉前面的多余的00
//
//        for (int k = 0; k < encryptLength - 1; k++) {
//            strTmp1 = pad(Integer.toHexString(DecodeInt[k]).toUpperCase(), 8, true);
//            DeQRResult.append(strTmp1);
//        }
//        if (nRemainder == 1) {
//            //余数为1，表示最后一个字符串，有2位
//
//            strTmp1 = pad(Integer.toHexString(DecodeInt[encryptLength - 1]).toUpperCase(), 2, true);
//            DeQRResult.append(strTmp1);
//        } else if (nRemainder == 2) {
//            //余数为2，表示最后一个字符串，有4位
//
//            strTmp1 = pad(Integer.toHexString(DecodeInt[encryptLength - 1]).toUpperCase(), 4, true);
//            DeQRResult.append(strTmp1);
//        } else if (nRemainder == 3) {
//            //余数为3，表示最后一个字符串，有6位
//
//            strTmp1 = pad(Integer.toHexString(DecodeInt[encryptLength - 1]).toUpperCase(), 6, true);
//            DeQRResult.append(strTmp1);
//        } else {
//            //余数为0，表示最后一个字符串，有8位，是完整的
//
//            strTmp1 = pad(Integer.toHexString(DecodeInt[encryptLength - 1]).toUpperCase(), 8, true);
//            DeQRResult.append(strTmp1);
//        }

//        System.out.println("解密后字符串" + DeQRResult);

        String QREndStr = strQRArray[0] + strQRArray[1] + strQRArray[2] + strQRArray[3] + EnQRResult;

//        System.out.println("完整二维码字符串：" + QREndStr);//1|00000001070300852953FCB0BFB18A162371996BCF295B3477E06C1C35990385A7CBBD04C945

        return QREndStr;

    }


    /**
     * 根据字符串 返回二维码 base64图片字符串
     *
     * @return 返回二维码 base64图片字符串
     */
    public static String createQRCodeBase64(String qr) {

        return Base64Util.getImgFileToBase64(qr);
    }

    /**
     * 根据用户 ID 密码 返回二维码 base64图片字符串
     *
     * @return 返回二维码 base64图片字符串
     */
    public static String createQRCodeBase64(String userId, String num) {

        String qrCodeImgFile = createQRCodeImgFile(userId, num);
        return Base64Util.getImgFileToBase64(qrCodeImgFile);
    }

    /**
     * 根据用户 ID 密码 返回二维码 base64图片字符串
     *
     * @param userId    用户 ID
     * @param macLast4  可离线开门的设备的 MAC后4位
     * @param pass      不重复的密码
     * @param startDate 密码有效开始时间
     * @param endDate   密码有效结束时间
     *
     * @return 返回二维码 base64图片字符串
     */
    public static String createQRCodeBase64(String userId, String macLast4, String pass, Date startDate, Date endDate) {

        String qrCodeImgFile = createQRCodeImgFile(userId, macLast4, pass, startDate, endDate);
        return Base64Util.getImgFileToBase64(qrCodeImgFile);
    }

    /**
     * 生成 QRCode 图片文件
     *
     * @return true 生成成功 / false 生成失败
     */
    public static String createQRCodeImgFile(String userId, String num) {
        String qr = createQRCode(userId, num);

        String userHomePath = System.getProperty("user.home");
        String imgPath = userHomePath + File.separator + "QRCode.png";

        try {
            boolean saveQRImg = QRCodeUtil.toQRImgFile(qr, imgPath, width, height);
            log.debug("生成二维码图片 " + (saveQRImg ? "成功" : "失败"));
            return imgPath;
        } catch (WriterException | IOException e) {
            e.printStackTrace();
            log.error("生成二维码门禁 QCCode 图片时出错  ---> " + e.getMessage() + e.getCause(), e);
        }
        return null;
    }

    /**
     * 生成 QRCode 图片文件
     *
     * @param userId    用户 ID
     * @param macLast4  可离线开门的设备的 MAC后4位
     * @param pass      不重复的密码
     * @param startDate 密码有效开始时间
     * @param endDate   密码有效结束时间
     * @return true 生成成功 / false 生成失败
     */
    public static String createQRCodeImgFile(String userId,String macLast4, String pass, Date startDate, Date endDate) {
        String qr = createQRCode(userId, macLast4, pass, startDate, endDate);

        String userHomePath = System.getProperty("user.home");
        String imgPath = userHomePath + File.separator + "QRCode"+ Math.random()* 9999 +".png";

        try {
            boolean saveQRImg = QRCodeUtil.toQRImgFile(qr, imgPath, width, height);
            log.debug("生成二维码图片 " + (saveQRImg ? "成功" : "失败"));

            return imgPath;
        } catch (WriterException | IOException e) {
            e.printStackTrace();
            log.error("生成二维码门禁 QCCode 图片时出错  ---> " + e.getMessage() + e.getCause(), e);
        }
        return null;
    }

    /**
     *@Author: timo
     *@Date: 2018/3/19 16:13
     *@Description:
     * 生成BAS64 位的二维码字符串
     *
     */
    public static String createQRCodeToBAS64String(String url, Integer width,Integer height){
        ByteArrayOutputStream bus = new ByteArrayOutputStream();
        try {
            ImageOutputStream ios = ImageIO.createImageOutputStream(bus);
            ImageIO.write(toBufferedImage(url,width,height),"png",ios);
            return ImageUtils.GetImageStr(new ByteArrayInputStream(bus.toByteArray()));
        } catch (IOException e) {
            e.printStackTrace();
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 二维码门禁测试
     */
    public static void main(String[] args) {

       // String qrCode = createQRCodeBase64("1", "15748674");
        //System.out.println("qrCode " + qrCode);
        System.out.println(createSignInQRCode("www.baidu.com"));
        //Base64Util.generateImage(qrCode.substring(22));

//        String qrCodeBase64 = createQRCodeBase64(qrCode);
//        System.out.println("qrCodeBase64 " + qrCodeBase64);

    }
}
